import datetime
import doctest
import re
import unittest

from django.core.urlresolvers import reverse
from django.test import TestCase

from htmock.models import *
import htmock.util.http
import htmock.util.restviews


def suite():
    suite = unittest.defaultTestLoader.loadTestsFromModule(htmock.tests)
    doctest_modules = (htmock.util.http, htmock.util.restviews)
    for mod in doctest_modules:
        suite.addTest(doctest.DocTestSuite(mod))
    return suite


class InspectTests(TestCase):
    def test_get_index(self):
        """GETting the inspect index"""
        resp = self.client.get(reverse('inspect-index'))
        self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8')
        self.assertContains(resp, u'htmock')
    
    def test_post_no_label(self):
        """POSTing a new resource with no label (should be generated)"""
        resp = self.client.post(reverse('inspect-index'),
            { 'label': '', 'ttl': '3', 'status': '404', 'max_header': '0' })
        mock = MockResource.objects.all()[0]
        self.assert_(re.match(u'^[a-z]+$', mock.label))
        self.assertEquals(mock.status_code, 404)
    
    def test_post(self):
        """POSTing a complete definition of a resource"""
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'ttl': '3.2', 'status': '0',
            'custom_status': '567', 'max_header': '5', 'header0': 'Allow',
            'value0': 'GET, HEAD', 'header3': 'Set-Cookie',
            'value3': 'baz=quux', 'body': 'Hi!'
        })
        mock = MockResource.objects.all()[0]
        self.assertEquals(mock.label, u'foo')
        self.assert_(mock.created_on < datetime.datetime.now())
        self.assertAlmostEquals(
            (mock.alive_until - mock.created_on).seconds / 3600.0, 3.2, 1
        )
        self.assertEquals(mock.status_code, 567)
        self.assertEquals(mock.headers.count(), 2)
        h = mock.headers.get(field_name=u'Allow')
        self.assertEquals(h.field_value, u'GET, HEAD')
        h = mock.headers.get(field_name=u'Set-Cookie')
        self.assertEquals(h.field_value, u'baz=quux')
        self.assertEquals(mock.entity_body, u'Hi!')
    
    def test_post_incomplete(self):
        """POSTing a form without a require field"""
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'status': '200', 'max_header': '0'
        })
        self.assertContains(resp, u'Time to live', status_code=403)
        self.assertEquals(MockResource.objects.count(), 0)
    
    def test_post_wrong_format(self):
        """POSTing a form with an error in field format"""
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'fo?o', 'ttl': '3.2', 'status': '200', 'body': 'Hi!'
        })
        self.assertContains(resp, u'Label', status_code=403)
        self.assertEquals(MockResource.objects.count(), 0)
    
    def test_post_duplicate_label(self):
        """POSTing a new resource with a duplicate label"""
        self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'ttl': '3', 'status': '200', 'max_header': '0',
            'body': 'Hi!'
        })
        self.assertEquals(MockResource.objects.count(), 1)
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'ttl': '3', 'status': '200', 'max_header': '0',
            'body': 'Hi!'
        })
        self.assertEquals(resp.status_code, 403)
        self.assertEquals(MockResource.objects.count(), 1)
    
    def test_post_invalid_header(self):
        """POSTing with an invalid header name"""
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'ttl': '3', 'status': '200', 'max_header': '0',
            'body': 'Hi!', 'header0': 'Super Header', 'value0': 'true'
        })
        self.assertContains(resp, u'header name', status_code=403)
        self.assertEquals(MockResource.objects.count(), 0)
    
    def test_post_orphan_header(self):
        """POSTing with a header value without a corresponding header name"""
        resp = self.client.post(reverse('inspect-index'), {
            'label': 'foo', 'ttl': '3', 'status': '200', 'max_header': '0',
            'body': 'Hi!', 'header0': '', 'value0': 'true'
        })
        mock = MockResource.objects.get(label=u'foo')
        self.assertEquals(mock.headers.count(), 0)
    
    def test_get_details(self):
        """GETting a mock resource's inspect page"""
        MockResource(label=u'baz', created_on=datetime.datetime.now(),
                     alive_until=(datetime.datetime.now() +
                                  datetime.timedelta(hours=1)),
                     status_code=200, entity_body=u'Hello!').save()
        resp = self.client.get(reverse('inspect-resource', args=(u'baz',)))
        self.assertContains(resp, u'Hit count: 0')
    
    def test_delete(self):
        """DELETEing a mock resource"""
        self.client.post(reverse('inspect-index'), {
            'label': 'baz', 'ttl': '3', 'status': '200', 'max_header': '0',
            'body': 'Hi!'
        })
        self.assertEqual(MockResource.objects.count(), 1)
        self.client.delete(reverse('inspect-resource', args=(u'baz',)))
        self.assertEqual(MockResource.objects.count(), 0)


class MockTests(TestCase):
    def test_hit(self):
        """Hitting a mock resource"""
        mock = MockResource(
            label=u'baz',
            created_on=datetime.datetime.now(),
            alive_until=(datetime.datetime.now() +
                         datetime.timedelta(hours=1)),
            status_code=201, entity_body=u'Hello!',
        )
        mock.save()
        Header(resource=mock, field_name=u'Content-Type',
               field_value=u'text/plain; charset=iso-8859-1').save()
        Header(resource=mock, field_name=u'Set-Cookie',
               field_value=u'qux=xyzzy').save()
        
        resp = self.client.post(reverse('mock', args=(u'baz',)) + u'?one=two',
                                { 'three': 'four', 'five': 'six' },
                                HTTP_ACCEPT='text/plain')
        self.assertEquals(resp.status_code, 201)
        self.assertEquals(resp['Content-Type'],
                          'text/plain; charset=iso-8859-1')
        self.assertEquals(resp['Set-Cookie'], 'qux=xyzzy')
        self.assertEquals(resp.content, 'Hello!')
        
        self.assertEquals(mock.requests.count(), 1)
        req = mock.requests.all()[0]
        self.assertEquals((datetime.datetime.now() - req.received_on).seconds,
                          0)
        self.assertEquals(req.remote_addr, u'127.0.0.1')
        self.assertEquals(req.method, u'POST')
        self.assertEquals(req.query_string, u'one=two')
        self.assertEquals(req.headers['Accept'], 'text/plain')
        self.assertEquals(len(req.get_params), 1)
        self.assertEquals(req.get_params['one'], 'two')
        self.assertEquals(len(req.post_params), 2)
        self.assertEquals(req.post_params['three'], 'four')
        self.assertEquals(req.post_params['five'], 'six')
